/* 
 * File:   Sqrt.cpp
 * Author: Gab'z
 * 
 * Created on 4 juin 2012, 09:38
 */

#include "Sqrt.h"
#include "../../../Context.h"
#include "../../literal/Stack.h"
#include "../../literal/Complex.h"
#include "../../literal/Real.h"
#include <complex>

namespace calculator {

    Sqrt::Sqrt(const std::string name) : UnaryOperator(name) {
    }

    Sqrt::Sqrt(const Sqrt& orig) : UnaryOperator(orig._name) {
    }

    Sqrt::~Sqrt() {
    }

    std::string Sqrt::isExecutable() const {
        if (Context::getInstance()->getCurrentStack()->size() == 0) return "Empty stack";
        const Complex* complex = dynamic_cast<const Complex*> (Context::getInstance()->getCurrentStack()->top());
        const SimpleNumber* sn = dynamic_cast<const SimpleNumber*> (complex);
        if (((complex != 0) || (sn != 0 && sn->value() < 0)) && Context::getInstance()->getParameters()->getComplexMode() == false) return "Complex mode OFF";
        return "";
    }

    Sqrt* Sqrt::clone() const {
        return new Sqrt(_name);
    }

    const Number* Sqrt::apply(const Number* number) const throw (ArithmeticException) {
        const SimpleNumber* sn = dynamic_cast<const SimpleNumber*> (number);
        const Complex* complex = dynamic_cast<const Complex*> (number);
        if (((complex != 0) || (sn != 0 && sn->value() < 0)) && Context::getInstance()->getParameters()->getComplexMode() == false) throw ArithmeticException("Complex mode OFF");
        if (sn != 0) {
            double x = (double) sn->value();
            if (x >= 0) {
                return new Real(std::sqrt(x));
            } else {
                // traitement d'un complexe ou un réel régatif
                const Complex* res;
                if (sn != 0) {
                    res = new Complex(sn);
                } else {
                    res = complex->clone();
                }
                std::complex<double> z(complex->getRe()->value(), complex->getIm()->value());
                z = std::sqrt(z);
                delete complex;
                const Real* r1 = new Real(std::real(z));
                const Real* r2 = new Real(std::imag(z));
                complex = new Complex(r1, r2);
                delete r1;
                delete r2;
                return complex;
            }
        }

        throw ArithmeticException("NaN");
    }

}